package com.allwees.bs.c.module.order.service.impl;

import com.allwees.bs.c.module.ops.service.CashActiveDefUseService;
import com.allwees.bs.c.module.ops.service.PromoUseService;
import com.allwees.bs.c.module.ops.service.UserCouponService;
import com.allwees.bs.c.module.order.context.bo.CouponComputeBo;
import com.allwees.bs.c.module.order.context.bo.CouponComputeItemBo;
import com.allwees.bs.c.module.order.context.bo.UserCouponBo;
import com.allwees.bs.c.module.order.context.compute.ComputeStrategy;
import com.allwees.bs.c.module.order.dao.CartDao;
import com.allwees.bs.c.module.order.dto.CartDTO;
import com.allwees.bs.c.module.order.dto.CartItemDTO;
import com.allwees.bs.c.module.order.entity.CartEntity;
import com.allwees.bs.c.module.order.entity.CartItemEntity;
import com.allwees.bs.c.module.order.entity.UserAddCartLogEntity;
import com.allwees.bs.c.module.order.repository.CartItemRepository;
import com.allwees.bs.c.module.order.repository.CartRepository;
import com.allwees.bs.c.module.order.repository.UserAddCartLogRepository;
import com.allwees.bs.c.module.order.req.CartCheckoutReq;
import com.allwees.bs.c.module.order.req.CartItemReq;
import com.allwees.bs.c.module.order.req.CartReq;
import com.allwees.bs.c.module.order.req.CartUseCashReq;
import com.allwees.bs.c.module.order.service.CartService;
import com.allwees.bs.c.module.product.dto.ProductDetailsDTO;
import com.allwees.bs.c.module.product.dto.SkuDTO;
import com.allwees.bs.c.module.product.service.ProductService;
import com.allwees.bs.c.module.user.entity.UserCashEntity;
import com.allwees.bs.c.module.user.entity.UserCouponEntity;
import com.allwees.bs.c.module.user.entity.UserPromoLogEntity;
import com.allwees.bs.c.module.user.service.UserCashService;
import com.allwees.bs.core.modelbase.constant.ResultEnum;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import com.allwees.core.common.util.DateUtil;
import com.allwees.support.marketing.handler.PriceAdjustor;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yangylsky
 */
@Service
@Slf4j
public class CartServiceImpl extends ServiceImpl<CartDao, CartEntity> implements CartService {

    @Autowired
    private CartRepository cartRepository;

    @Autowired
    private CartItemRepository cartItemRepository;

    @Autowired
    private UserAddCartLogRepository userAddCartLogRepository;

    @Autowired
    private ProductService productService;

    @Autowired
    private UserCashService userCashService;
    @Autowired
    private UserCouponService userCouponService;
    @Autowired
    private CashActiveDefUseService cashActiveDefUseService;
    @Autowired
    private PromoUseService promoUseService;
    @Autowired
    private ComputeStrategy computeStrategy;

    private CartEntity getCartByUuid(String cartUuid) {
        CartEntity entity = cartRepository.getByUuid(cartUuid);
        if (entity == null) {
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }
        List<CartItemEntity> items = cartItemRepository.getByCartId(cartUuid);
        entity.setItems(items == null ? new ArrayList<>() : items);
        return entity;
    }

    private CartEntity getUserCart(String userUuid, boolean createIfNotExsits) {
        CartEntity entity = cartRepository.getByUser(userUuid);
        if (entity == null) {
            if (createIfNotExsits) {
                entity = new CartEntity();
                entity.setUserUuid(userUuid);
                this.save(entity);
                return entity;
            }
            throw BusinessException.getInstance(ResultEnum.NOT_FOUND);
        }
        List<CartItemEntity> items = cartItemRepository.getByCartId(entity.getUuid());
        entity.setItems(items);
        return entity;
    }

    @Override
    public CartDTO getCartByUser(String userUuid) {
        return new CartDTO(getUserCart(userUuid, true));
    }

    @Override
    public CartDTO getCart(String cartUuid) {
        return getCart(cartUuid, true);
    }

    @Override
    public CartDTO getCart(String cartUuid, boolean useStrategy) {
        CartDTO cart = new CartDTO(getCartByUuid(cartUuid));

        UserCashEntity userCash = userCashService.getCash(cart.getUserUuid());
        if (userCash != null && userCash.getValue() != null) {
            cart.setUserAmtCash(userCash.getValue());
        }

        if (CollectionUtils.isEmpty(cart.getItems())) {
            return cart;
        }
        if (useStrategy) {
            strategy(cart);
        }

        return cart;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void clearCart(String cartUuid) {
        CartEntity entity = getCartByUuid(cartUuid);
        clearCartItems(entity);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void clearUserCart(String userUuid) {
        CartEntity entity = getUserCart(userUuid, false);
        entity.setUserCouponUuid(null);
        entity.setUserCouponNo(null);
        entity.setPromoCode(null);
        if (null != entity.getLocked() && entity.getLocked() != 0) {
            //购物车解冻
            entity.setLocked(0);
        }
        entity.setUserPromoLogUuid(null);
        entity.setAmtCash(BigDecimal.ZERO);
        this.updateById(entity);

        clearCartItems(entity);
    }

    private void clearCartItems(CartEntity entity) {
        if (entity != null && entity.getItems() != null) {
            entity.getItems().forEach(Item -> cartItemRepository.delete(Item));
        }
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public CartDTO addSku(String cartUuid, String skuUuid, Integer count, String userUuid, String channelUuid) {
        getCartByUuid(cartUuid);
        SkuDTO sku = productService.findSkuByUuid(skuUuid);
        if (sku.getQuantity() < count) {
            throw BusinessException.getInstance(ResultEnum.NO_STOCK_ERROR);
        }
        CartItemEntity entity = cartItemRepository.findByCartAndSku(cartUuid, skuUuid);
        if (entity == null) {

            entity = CartItemEntity.builder()
                    .cartUuid(cartUuid)
                    .productUuid(sku.getProductUuid())
                    .skuUuid(sku.getUuid())
                    .quantity(count)
                    .build();
        } else {
            entity.setQuantity(entity.getQuantity() + count);
        }
        cartItemRepository.save(entity);
        UserAddCartLogEntity userAddCartLogEntity = new UserAddCartLogEntity();
        userAddCartLogEntity.setCreatedAt(DateUtil.now());
        userAddCartLogEntity.setCartUuid(cartUuid);
        userAddCartLogEntity.setSkuUuid(skuUuid);
        userAddCartLogEntity.setUserUuid(userUuid);
        userAddCartLogRepository.save(userAddCartLogEntity);
        return getCart(cartUuid);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public CartDTO reduceSku(String cartUuid, String skuUuid, Integer count, String channelUuid) {
        getCartByUuid(cartUuid);
        CartItemEntity entity = cartItemRepository.findByCartAndSku(cartUuid, skuUuid);
        if (entity != null) {
            if (entity.getQuantity() > count) {
                entity.setQuantity(entity.getQuantity() - count);
                cartItemRepository.save(entity);
            } else {
                cartItemRepository.delete(entity);
            }
        }
        return getCart(cartUuid);
    }

    @Override
    public CartDTO deleteCartItem(CartReq req) {
        String cartUuid = req.getCartUuid();
        getCartByUuid(cartUuid);
        CartItemEntity entity = cartItemRepository.findByCartAndSku(cartUuid, req.getSkuUuid());
        if (entity != null) {
            cartItemRepository.delete(entity);
        }
        return getCart(cartUuid);
    }

    @Override
    public CartDTO calculateCartItems(List<CartItemReq> itemVos) {
        CartDTO cart = new CartDTO();
        List<CartItemDTO> items = _buildCartItems(itemVos);
        cart.setItems(items);
        cart.setItemsWithStock(items);
        cart.setSum(items.stream().mapToInt(CartItemDTO::getQuantity).sum());

        cart.setAmtProduct(cart.amtProduct());
        cart.setOriginalAmtProduct(cart.getAmtProduct());

        cart.setAmtShipping(cart.amtShipping());
        cart.setOriginalAmtShipping(cart.getAmtShipping());

        cart.setAmt(cart.amt());

        strategy(cart);

        return cart;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public CartDTO mergeCartItems(String cartUuid, List<CartItemReq> itemVos, String userUuid, String channelUuid) {
        CartEntity cartEntity = getCartByUuid(cartUuid);
        for (CartItemReq itemVo : itemVos) {
            CartItemEntity item = cartEntity.addItem(itemVo);
            cartItemRepository.save(item);
            UserAddCartLogEntity userAddCartLogEntity = new UserAddCartLogEntity();
            userAddCartLogEntity.setCreatedAt(DateUtil.now());
            userAddCartLogEntity.setCartUuid(cartUuid);
            userAddCartLogEntity.setSkuUuid(item.getSkuUuid());
            userAddCartLogEntity.setUserUuid(userUuid);
            userAddCartLogRepository.save(userAddCartLogEntity);
        }
        cartRepository.save(cartEntity);
        return getCart(cartUuid);
    }

    private List<CartItemDTO> _buildCartItems(List<CartItemReq> itemVos) {
        if (CollectionUtils.isEmpty(itemVos)) {
            return new ArrayList<>(0);
        }

        List<CartItemDTO> itemDtos = new ArrayList<>();
        itemVos.forEach(itemVo -> {
            SkuDTO sku = productService.findSkuByUuid(itemVo.getSkuUuid());
            CartItemDTO itemDto;
            if (sku.getQuantity() > 0) {
                itemDto = new CartItemDTO();
                itemDto.setQuantity(itemVo.getQuantity());
                PriceAdjustor.adjustPrice(sku);
                itemDto.setSku(sku.filter(false));
                ProductDetailsDTO product = productService.findProductByUuid(sku.getProductUuid());
                itemDto.setProduct(product.filter(false));
                itemDtos.add(itemDto);
            }
        });
        return itemDtos;
    }

    private CartDTO strategy(CartDTO cart) {
        UserCouponBo userCoupon = findCoupon(cart.getUserCouponUuid(), cart.getUserPromoLogUuid());
        if (null != userCoupon) {
            cart.setDiscountRate(userCoupon.getFaceValue());
        }
        CouponComputeBo computeBo = new CouponComputeBo(cart)
                .setItems(cart.getItemsWithStock().stream().map(CouponComputeItemBo::new).collect(Collectors.toList()));

        computeStrategy.compute(userCoupon, computeBo).rest(cart);
        return cart;
    }

//    /**
//     * 计算金额
//     *
//     * @return
//     */
//    @Override
//    @Transactional
//    public CartDTO checkout(CartCheckoutReq req) {
//
//        updateCouponUuid(req.getCartUuid(), req.getCartUuid(), req.getUserCouponUuid(), "", "");
//
//        return getCart(req.getCartUuid(), true);
//    }


    @Override
    @Transactional
    public CartDTO useCoupon(String userUuid, CartCheckoutReq req) {
        String userCouponUuid = req.getUserCouponUuid(), userCouponNo = null, userPromoLogUuid = null;
        String promoCode = req.getPromoCode();
        if (StringUtils.isNotBlank(promoCode)) {
            UserCouponEntity userCoupon = userCouponService.receiveAndUse(userUuid, promoCode);
            if (userCoupon != null) {
                userCouponUuid = userCoupon.getUuid();
            } else {
                UserPromoLogEntity userPromoLog = promoUseService.receiveAndUse(userUuid, promoCode);
                if (userPromoLog == null) {
                    throw BusinessException.getInstance(ResultEnum.COUPON_NOT_FOUND);
                }
                userPromoLogUuid = userPromoLog.getUuid();
            }
        } else if (StringUtils.isNotBlank(userCouponUuid)) {
            UserCouponEntity userCoupon = userCouponService.use(userCouponUuid);
            if (userCoupon != null) {
                userCouponNo = userCoupon.getNo();
            }
        }
        updateCouponUuid(req.getCartUuid(), userCouponUuid, userCouponNo, userPromoLogUuid, promoCode);
        return getCart(req.getCartUuid(), true);
    }

    @Override
    public CartDTO useCash(String userUuid, CartUseCashReq req) {
        cashActiveDefUseService.checkByUserUuid(userUuid, req.getCartUuid(), req.getAmtCash());
        updateUserCash(req.getCartUuid(), req.getAmtCash());
        return getCart(req.getCartUuid(), true);
    }


    /**
     * 使用cash支付
     *
     * @param cartUuid
     * @param amtCash
     */
    private void updateUserCash(String cartUuid, BigDecimal amtCash) {
        CartEntity cart = cartRepository.getByUuid(cartUuid);
        cart.setAmtCash(amtCash);
        cartRepository.save(cart);
    }

    /**
     * 使用付款码支付
     *
     * @param cartUuid
     * @param userCouponUuid
     */
    private void updateCouponUuid(String cartUuid, String userCouponUuid, String userCouponNo, String userPromoLogUuid, String promoCode) {
        CartEntity cart = cartRepository.getByUuid(cartUuid);
        cart.setUserCouponUuid(userCouponUuid);
        cart.setUserCouponNo(userCouponNo);
        cart.setPromoCode(promoCode);
        cart.setUserPromoLogUuid(userPromoLogUuid);
        this.updateById(cart);
    }

    private UserCouponBo findCoupon(String userCouponUuid, String userPromoLogUuid) {
        UserCouponBo userCoupon = null;
        if (StringUtils.isNotBlank(userCouponUuid)) {
            userCoupon = new UserCouponBo(userCouponService.load(userCouponUuid));
        } else if (StringUtils.isNotBlank(userPromoLogUuid)) {
            userCoupon = new UserCouponBo(promoUseService.findByLogUuid(userPromoLogUuid));
        }
        return userCoupon;
    }
}
